/* quiccTimer.c - QUICC timer library */

/* Copyright 1984-2005 Wind River Systems, Inc. */

/*

modification history
--------------------
01a,31jan05,gtf  written starting with template.
*/

/*
DESCRIPTION


INCLUDES:
timestampDev.h
*/

/* includes */

#include "vxWorks.h"
#include "config.h"
#include "drv/timer/timerDev.h"
#include "drv/timer/timestampDev.h"
#include "quiccIntrCtl.h"
#include "quiccTimer.h"

/* defines */

#ifndef TIMESTAMP_HZ
#define TIMESTAMP_HZ	333000000			 /* timestamp counter freq */
#endif

/* The default is to assume memory mapped I/O */

#ifndef TEMPLATE_READ
#define TEMPLATE_READ(reg, result) \
	(result = *reg)
#endif /*TEMPLATE_READ*/

#ifndef TEMPLATE_WRITE
#define TEMPLATE_WRITE(reg, data) \
	(*reg = data)
#endif /*TEMPLATE_WRITE*/


/* locals */

LOCAL FUNCPTR sysClkRoutine	= NULL; /* routine to call on clock tick */
LOCAL int sysClkArg		= 0; /* its argument */
LOCAL BOOL sysClkRunning	= FALSE;
LOCAL BOOL sysClkConnectFirstTime	= TRUE;
LOCAL int sysClkTicksPerSecond	= 60;
LOCAL int decCountVal	= 10000000;	/* default dec value */
LOCAL int sysDecClkFrequency	= 0;

#ifdef INCLUDE_AUX_CLK
LOCAL FUNCPTR sysAuxClkRoutine	= NULL;
LOCAL int sysAuxClkArg		= 0;
LOCAL BOOL sysAuxClkRunning	= FALSE;
LOCAL int sysAuxClkTicksPerSecond = 100;
#endif

#ifdef INCLUDE_TIMESTAMP

LOCAL BOOL	sysTimestampRunning	= FALSE; /* running flag */
LOCAL int	sysTimestampPeriodValue	= 0;	 /* Max counter value */
LOCAL FUNCPTR	sysTimestampRoutine	= NULL;  /* routine to call on intr */
LOCAL int	sysTimestampArg		= 0;     /* arg for routine */
LOCAL int sysTimestampTicksPerSecond = 100;

void	sysTimestampInt (void);		 /* forward declaration */

#endif  /* INCLUDE_TIMESTAMP */


/*******************************************************************************
*
* sysClkInt - interrupt level processing for system clock
*
* This routine handles an auxiliary clock interrupt.  It acknowledges the
* interrupt and calls the routine installed by sysClkConnect().
*/

void sysClkInt (void)
    {
    vxDecReload (decCountVal); /* reload the decrementer */

    /* execute the system clock routine */

    if (sysClkRunning && (sysClkRoutine != NULL))
	(*(FUNCPTR) sysClkRoutine) (sysClkArg);


    }


/*******************************************************************************
*
* sysClkConnect - connect a routine to the system clock interrupt
*
* This routine specifies the interrupt service routine to be called at each
* clock interrupt.  Normally, it is called from usrRoot() in usrConfig.c to 
* connect usrClock() to the system clock interrupt.
*
* RETURN: OK, or ERROR if the routine cannot be connected to the interrupt.
*
* SEE ALSO: intConnect(), usrClock(), sysClkEnable()
*/

STATUS sysClkConnect
    (
    FUNCPTR routine,	/* routine to be called at each clock interrupt */
    int arg		/* argument with which to call routine */
    )
    {
    if (sysClkConnectFirstTime)
	{
	sysHwInit2();
	sysClkConnectFirstTime = FALSE;
        excIntConnect ((VOIDFUNCPTR *) _EXC_OFF_DECR, (VOIDFUNCPTR) sysClkInt);
	}

    sysClkRoutine	= routine;
    sysClkArg		= arg;

    return (OK);
    }


/*******************************************************************************
*
* sysClkDisable - turn off system clock interrupts
*
* This routine disables system clock interrupts.
*
* RETURNS: N/A
*
* SEE ALSO: sysClkEnable()
*/

void sysClkDisable (void)
    {
    if (sysClkRunning)
	sysClkRunning = FALSE;

    }



/*******************************************************************************
*
* sysClkEnable - turn on system clock interrupts
*
* This routine enables system clock interrupts.
*
* RETURNS: N/A
*
* SEE ALSO: sysClkConnect(), sysClkDisable(), sysClkRateSet()
*/

void sysClkEnable (void)
    {

    if (!sysClkRunning)
	{
	sysClkRunning = TRUE;
	vxDecSet (decCountVal);
	}
    }


/*******************************************************************************
*
* sysClkRateGet - get the system clock rate
*
* This routine returns the system clock rate.
*
* RETURNS: The number of ticks per second of the system clock.
*
* SEE ALSO: sysClkEnable(), sysClkRateSet()
*/

int sysClkRateGet (void)
    {
    return (sysClkTicksPerSecond);
    }


/*******************************************************************************
*
* sysClkRateSet - set the system clock rate
*
* This routine sets the interrupt rate of the system clock.
* It is called by usrRoot() in usrConfig.c.
*
* RETURNS: OK, or ERROR if the tick rate is invalid or the timer cannot be set.
*
* SEE ALSO: sysClkEnable(), sysClkRateGet()
*/

STATUS sysClkRateSet
    (
    int ticksPerSecond	    /* number of clock interrupts per second */
    )
    {

    if (ticksPerSecond < SYS_CLK_RATE_MIN || ticksPerSecond > SYS_CLK_RATE_MAX)
        return (ERROR);

    /* save the clock speed */

    sysClkTicksPerSecond = ticksPerSecond;

    /* Calibrate the clock, if needed. */

#ifdef PPC_TMR_RATE_SET_ADJUST
    PPC_TMR_RATE_SET_ADJUST;
#endif

    sysDecClkFrequency = DEC_CLOCK_FREQ / DEC_CLK_TO_INC;

    /* 
     * compute the value to load in the decrementer. The new value will be
     * loaded into the decrementer after the current period
     */

    decCountVal = sysDecClkFrequency / ticksPerSecond;


    return (OK);
    }




#ifdef INCLUDE_AUX_CLK
/* In cascade mode:

If GTCFR1[PCAS] = 1 and/or GTCFR2[SCAS] = 1, the two 16-bit timers (timer 1 and timer 2 or
timer 3 and timer4) function as a 32-bit timer with a 32-bit GTRFR, GTCPR, and GTCNR. In this
case, GTMDR1/GTMDR3 is ignored, and the modes and functions are defined using
GTMDR2/GTMDR4 and GTCFR1/GTCFR2. The capture are controlled from TIN2, and the
interrupts are generated from GTEVR2/GTEVR4. When working in the pair-cascaded mode, the cascaded
GTRFR, GTCPR, and GTCNR should be referenced with 32-bit bus cycles.
*/


/*******************************************************************************
*
* sysAuxClkInt - handle an auxiliary clock interrupt
*
* This routine handles an auxiliary clock interrupt.  It acknowledges the
* interrupt and calls the routine installed by sysAuxClkConnect().
*
* RETURNS: N/A
*/

void sysAuxClkInt (void)
    {
    /* TODO - acknowledge the interrupt if needed */
    /* call auxiliary clock service routine */

    if (sysAuxClkRoutine != NULL)
	    (*sysAuxClkRoutine) (sysAuxClkArg);

    *QUICC_GTEVR2(CCSBAR, QUICC_GTM1_BASE) |= 0x3 ;
    }

/*******************************************************************************
*
* sysAuxClkConnect - connect a routine to the auxiliary clock interrupt
*
* This routine specifies the interrupt service routine to be called at each
* auxiliary clock interrupt.  It does not enable auxiliary clock interrupts.
*
* RETURNS: OK, or ERROR if the routine cannot be connected to the interrupt.
*
* SEE ALSO: intConnect(), sysAuxClkEnable()
*/

STATUS sysAuxClkConnect
    (
    FUNCPTR routine,    /* routine called at each aux clock interrupt */
    int arg             /* argument to auxiliary clock interrupt routine */
    )
    {
    sysAuxClkRoutine	= NULL;
    sysAuxClkArg	= arg;
    sysAuxClkRoutine	= routine;

    return (OK);
    }


void bspTestAuxIntFun(void)
{
  printf("Aux timer Interrupt.\n");
}

void bspTestAuxInt(void)
{
  sysAuxClkConnect((FUNCPTR)bspTestAuxIntFun,0);
}
/*******************************************************************************
*
* sysAuxClkDisable - turn off auxiliary clock interrupts
*
* This routine disables auxiliary clock interrupts.
*
* RETURNS: N/A
*
* SEE ALSO: sysAuxClkEnable()
*/

void sysAuxClkDisable (void)
    {
    if (sysAuxClkRunning)
        {
    	/* TODO - disable auxiliary timer interrupts */
        /* disable interrupt */
        intDisable(INUM_GTM2) ;
        /* stop timer */
        *QUICC_GTMDR2(CCSBAR, QUICC_GTM1_BASE) = 0 ;
        *QUICC_GTCNR1_2(CCSBAR,QUICC_GTM1_BASE) = 0 ;
	    sysAuxClkRunning = FALSE;
        }
    }



/*******************************************************************************
*
* sysAuxClkEnable - turn on auxiliary clock interrupts
*
* This routine enables auxiliary clock interrupts.
*
* RETURNS: N/A
*
* SEE ALSO: sysAuxClkConnect(), sysAuxClkDisable(), sysAuxClkRateSet()
*/

void sysAuxClkEnable (void)
    {
    static BOOL connected = FALSE;
  
    if (!connected)
	    {
	    intConnect(INUM_TO_IVEC(INUM_GTM2), sysAuxClkInt, 0) ;
	    connected = TRUE;
	    }
     
    if (!sysAuxClkRunning)
        {
    	/*
    	 * TODO - start auxiliary timer interrupts at a
    	 * at a frequency of sysAuxClkTicksPerSecond.
    	 */

        *QUICC_GTCFR1(CCSBAR, QUICC_GTM1_BASE) = ((0x1<<7)|0x11) ;

        /* set secondary prescalar - no prescaling */
        *QUICC_GTMDR2(CCSBAR, QUICC_GTM1_BASE) = 0 ;

        /* set global prescalar - no prescaling */
        *QUICC_GTPSR2(CCSBAR, QUICC_GTM1_BASE) = 0 ;

        /* set reference value */
       *QUICC_GTRFR1_2(CCSBAR,QUICC_GTM1_BASE) = (UINT32)(SYS_CLK_FREQ / sysAuxClkTicksPerSecond) ;

        /* set counter register to 0 (counts up) */
        *QUICC_GTCNR1_2(CCSBAR,QUICC_GTM1_BASE) = 0 ;

        /* interrupt on reaching reference value, restart count when ref value reached. */
        /* secondary prescalar is div by 1 */
        *QUICC_GTMDR2(CCSBAR, QUICC_GTM1_BASE) |= ((0x1<<4)|(0x1<<3)|(0x1<<1));

        intEnable(INUM_GTM2) ;
      
    	sysAuxClkRunning = TRUE;
	    }
    }


/*******************************************************************************
*
* sysAuxClkRateGet - get the auxiliary clock rate
*
* This routine returns the interrupt rate of the auxiliary clock.
*
* RETURNS: The number of ticks per second of the auxiliary clock.
*
* SEE ALSO: sysAuxClkEnable(), sysAuxClkRateSet()
*/

int sysAuxClkRateGet (void)
    {
    return (sysAuxClkTicksPerSecond);
    }



/*******************************************************************************
*
* sysAuxClkRateSet - set the auxiliary clock rate
*
* This routine sets the interrupt rate of the auxiliary clock.  It does not
* enable auxiliary clock interrupts.
*
* RETURNS: OK, or ERROR if the tick rate is invalid or the timer cannot be set.
*
* SEE ALSO: sysAuxClkEnable(), sysAuxClkRateGet()
*/

STATUS sysAuxClkRateSet
    (
    int ticksPerSecond  /* number of clock interrupts per second */
    )
    {
    if (ticksPerSecond < AUX_CLK_RATE_MIN || ticksPerSecond > AUX_CLK_RATE_MAX)
        return (ERROR);

    /* TODO - return ERROR if rate is not supported */

    sysAuxClkTicksPerSecond = ticksPerSecond ;

    if (sysAuxClkRunning)
	    {
	    sysAuxClkDisable ();
	    sysAuxClkEnable ();
	    }

    return (OK);
    }

#endif /* INCLUDE_AUX_CLK */



#ifdef  INCLUDE_TIMESTAMP


/*
 * TODO - This is an example timestamp driver.  It must be modified to
 * work with the specific hardware in use.  There is an application note
 * on creating a timestamp driver that is very useful.
 */

/*******************************************************************************
*
* sysTimestampInt - timestamp timer interrupt handler
*
* This rountine handles the timestamp timer interrupt.  A user routine is
* called, if one was connected by sysTimestampConnect().
*
* RETURNS: N/A
*
* SEE ALSO: sysTimestampConnect()
*/

void sysTimestampInt (void)
    {
    if (sysTimestampRunning && sysTimestampRoutine != NULL)
	    (*sysTimestampRoutine)(sysTimestampArg);

    *QUICC_GTEVR4(CCSBAR, QUICC_GTM1_BASE) |= 0x3 ;
    }

/*******************************************************************************
*
* sysTimestampConnect - connect a user routine to the timestamp timer interrupt
*
* This routine specifies the user interrupt routine to be called at each
* timestamp timer interrupt.  It does not enable the timestamp timer itself.
*
* RETURNS: OK, or ERROR if sysTimestampInt() interrupt handler is not used.
*/

STATUS sysTimestampConnect
    (
    FUNCPTR routine,    /* routine called at each timestamp timer interrupt */
    int arg     /* argument with which to call routine */
    )
    {
    sysTimestampRoutine = NULL;
    sysTimestampArg = arg;
    sysTimestampRoutine = routine;

    return OK;
    }


/*******************************************************************************
*
* sysTimestampEnable - initialize and enable the timestamp timer
*
* This routine connects the timestamp timer interrupt and initializes the
* counter registers.  If the timestamp timer is already running, this routine
* merely resets the timer counter.
*
* The rate of the timestamp timer should be set explicitly within the BSP,
* in the sysHwInit() routine.  This routine does not intialize the timer
* rate.
*
* RETURNS: OK, or ERROR if hardware cannot be enabled.
*/

STATUS sysTimestampEnable (void)
    {
    static BOOL connected = FALSE;

    if (!connected)
	    {
	    /* TODO - connect sysTimestampInt to the appropriate interrupt */
	    intConnect(INUM_TO_IVEC(INUM_GTM4), sysTimestampInt, 0) ;
	    connected = TRUE;
	    }

    if (!sysTimestampRunning)
        {
        /* Set the timestamp timer to the desired timestamp freq. Don't base it off
           of the system clock. */
        *QUICC_GTCFR2(CCSBAR, QUICC_GTM1_BASE) = ((0x1<<7)|0x11) ;

        /* Use prescalars to set timestamp clock frequency, these divide the system clock. */
        /* We want the clock to run @ the requested timestamp frequency */
        /* divider(s) = SYS_CLK_FREQ / TIMESTAMP_FREQ */
        /* SPS = divide by 1->256 */
        /* PPS = divide by 1->256 */
        /* 330 MHz / 256 / 256 = 5035 Hz */

        /* set secondary prescalar - no prescaling */
        *QUICC_GTMDR4(CCSBAR, QUICC_GTM1_BASE) = 0 ;

        /* set global prescalar - no prescaling */
        *QUICC_GTPSR4(CCSBAR, QUICC_GTM1_BASE) = 0 ;


        /* set reference value */
    
        /* timstamp_period (sec) = 1/SYS_CLK_FREQ * count */
        /* count = SYS_CLK_FREQ * timestamp_period */

        /* 32 bit counter @ 330MHz, period = 6.442 seconds. */
        *QUICC_GTRFR3_4(CCSBAR,QUICC_GTM1_BASE) = (UINT32)(SYS_CLK_FREQ / sysTimestampTicksPerSecond);

        /* set counter register to 0 (counts up) */
        *QUICC_GTCNR3_4(CCSBAR,QUICC_GTM1_BASE) = 0 ;

    	/* TODO - setup the counter registers and start the counter */

        /* interrupt on reaching reference value, restart count when ref value reached. */
        /* secondary prescalar is div by 1 */
        *QUICC_GTMDR4(CCSBAR, QUICC_GTM1_BASE) |= ((0x1<<4)|(0x1<<3)|(0x1<<1));

        intEnable(INUM_GTM4) ;
	
	sysTimestampRunning = TRUE;
	}

    return (OK);
    }



/*******************************************************************************
*
* sysTimestampDisable - disable the timestamp timer
*
* This routine disables the timestamp timer.  Interrupts are not disabled,
* although the tick counter will not increment after the timestamp timer
* is disabled, thus interrupts will no longer be generated.
*
* RETURNS: OK, or ERROR if timer cannot be disabled.
*/

STATUS sysTimestampDisable (void)
    {
    if (sysTimestampRunning)
        {
        sysTimestampRunning = FALSE;

        intDisable(INUM_GTM4) ;

        /* stop timer */
        *QUICC_GTMDR4(CCSBAR, QUICC_GTM1_BASE) = 0 ;

        /* zero counter */
        *QUICC_GTCNR3_4(CCSBAR,QUICC_GTM1_BASE) = 0 ;
        }

    return (OK);
    }



/*******************************************************************************
*
* sysTimestampPeriod - get the timestamp timer period
*
* This routine returns the period of the timestamp timer in ticks.
* The period, or terminal count, is the number of ticks to which the timestamp
* timer will count before rolling over and restarting the counting process.
*
* RETURNS: The period of the timestamp timer in counter ticks.
*/

UINT32 sysTimestampPeriod (void)
    {
    /* TODO - this is example code, it must be replaced. */

    sysTimestampPeriodValue = 0x7fffffff ;

    return (sysTimestampPeriodValue);
    }



/*******************************************************************************
*
* sysTimestampFreq - get the timestamp timer clock frequency
*
* This routine returns the frequency of the timer clock, in ticks per second.
*
* RETURNS: The timestamp timer clock frequency, in ticks per second.
*/

UINT32 sysTimestampFreq (void)
    {
    /* TODO - this is example code, it must be replaced. */

    return (TIMESTAMP_HZ);
    }


/* input clock is 3nsec -> 12.885 seconds to count from 0..0xffffffff */



/*******************************************************************************
*
* sysTimestamp - get the timestamp timer tick count
*
* This routine returns the current value of the timestamp timer tick counter.
* The tick count can be converted to seconds by dividing by the return of
* sysTimestampFreq().
*
* This routine should be called with interrupts locked.  If interrupts are
* not already locked, sysTimestampLock() should be used instead.
*
* RETURNS: The current timestamp timer tick count.
*
* SEE ALSO: sysTimestampLock()
*/

UINT32 sysTimestamp (void)
    {
    UINT32 count = 0;

    /* TODO - read the timestamp timer value */
    count = *QUICC_GTCNR3_4(CCSBAR, QUICC_GTM1_BASE) ;

    return (count);
    }



/*******************************************************************************
*
* sysTimestampLock - get the timestamp timer tick count
*
* This routine returns the current value of the timestamp timer tick counter.
* The tick count can be converted to seconds by dividing by the return of
* sysTimestampFreq().
*
* This routine locks interrupts for cases where it is necessary to stop the
* tick counter in order to read it, or when two independent counters must
* be read.  If interrupts are already locked, sysTimestamp() should be
* used instead.
*
* RETURNS: The current timestamp timer tick count.
*
* SEE ALSO: sysTimestamp()
*/

UINT32 sysTimestampLock (void)
    {
    UINT32 result;
    int oldLevel;

    /*
     * TODO - If interrupt lockout is not needed, then don't do it. Just
     * return the value from sysTimestamp().
     */

    oldLevel = intLock ();

    result = sysTimestamp ();

    intUnlock (oldLevel);

    return (result);
    }

#endif  /* INCLUDE_TIMESTAMP */

